//#include "StdAfx.h"
// Copyright (C) 2002-2008 Nikolaus Gebhardt
// This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in irrlicht.h

#include "CFileSystem.h"
#include "IReadFile.h"
//#include "IWriteFile.h"
#include "CXMLReader.h"
#include "CAttributes.h"

#include "stdio.h"

#ifdef _IRR_XBOX_PLATFORM_
	#include <direct.h> // for _chdir
#elif defined (_IRR_WINDOWS_API_)
	#if !defined ( _WIN32_WCE )
		#include <direct.h> // for _chdir
	#endif
#else

	//#include <unistd.h>
	#include <limits.h>
	#include <stdlib.h>
#endif


//! constructor
CFileSystem::CFileSystem()
{
	#ifdef _DEBUG
	setDebugName("CFileSystem");
	#endif
#if defined(_IRR_PS3_PLATFORM_)
    memset(WorkingDirectory,0,sizeof(WorkingDirectory));
    unsigned int applicationType;
    unsigned int applicationAttr;
    CellGameContentSize gameContentSize;
    memset(&gameContentSize, 0x00, sizeof(CellGameContentSize));
	int ret = cellGameBootCheck(&applicationType, &applicationAttr, &gameContentSize, NULL);
    if(ret != CELL_OK)
    {
        os::Printer::log("cellGameBootCheck error",ELL_ERROR);
    }
    else
    {
        char contentPath[CELL_GAME_PATH_MAX];
        char dirPath[CELL_GAME_PATH_MAX];
        memset(contentPath, 0, sizeof(char) * CELL_GAME_PATH_MAX);
        memset(dirPath, 0, sizeof(char)* CELL_GAME_PATH_MAX);
		ret = cellGameContentPermit(contentPath, dirPath);
		strcpy(WorkingDirectory, dirPath);
		//strcpy(contentinfoPath, contentPath);
    }
    if (applicationAttr == CELL_GAME_ATTRIBUTE_APP_HOME ||
        applicationAttr == CELL_GAME_ATTRIBUTE_DEBUG)
    {
#if 0
		char tmpStr[128];
		memset(tmpStr, 0, sizeof(char) * 128);
		sprintf(tmpStr, "/dev_hdd0/game/NPUB30120/USRDIR");
		strcpy(WorkingDirectory, tmpStr);
#else
        strcpy(WorkingDirectory, "/app_home");
#endif
    }
    if(strlen(WorkingDirectory)==0) strcpy(WorkingDirectory,".");
    strcat(WorkingDirectory, "/");
    os::Printer::log("WorkingDirectory is:",WorkingDirectory, ELL_INFORMATION);
#endif
}


//! destructor
CFileSystem::~CFileSystem()
{
	u32 i;

	/*for ( i=0; i<ZipFileSystems.size(); ++i)
		ZipFileSystems[i]->drop();

	for ( i=0; i<PakFileSystems.size(); ++i)
		PakFileSystems[i]->drop();

	for ( i= 0; i<UnZipFileSystems.size(); ++i)
		UnZipFileSystems[i]->drop();*/
}


//! opens a file for read access
IReadFile* CFileSystem::createAndOpenFile(const c8* filename)
{
	IReadFile* file = 0;
	u32 i;

	/*for ( i=0; i<ZipFileSystems.size(); ++i)
	{
		file = ZipFileSystems[i]->openFile(filename);
		if (file)
			return file;
	}

	for ( i = 0; i<PakFileSystems.size(); ++i)
	{
		file = PakFileSystems[i]->openFile(filename);
		if (file)
			return file;
	}

	for ( i = 0; i<UnZipFileSystems.size(); ++i)
	{
		file = UnZipFileSystems[i]->openFile(filename);
		if (file)
			return file;
	}*/

	return createReadFile(filename);
}


//! Creates an IReadFile interface for treating memory like a file.
//IReadFile* CFileSystem::createMemoryReadFile(void* memory, s32 len, 
//			const c8* fileName, bool deleteMemoryWhenDropped)
//{
//	if (!memory)
//		return 0;
//	else
//		return irrnew CMemoryReadFile(memory, len, fileName, deleteMemoryWhenDropped);
//}


//! Opens a file for write access.
//IWriteFile* CFileSystem::createAndWriteFile(const c8* filename, bool append)
//{
//	return createWriteFile(filename, append);
//}


//bool CFileSystem::addFolderFileArchive(const c8* filename, bool ignoreCase, bool ignorePaths)
//{
//	CUnZipReader* zr = irrnew CUnZipReader(this, filename, ignoreCase, ignorePaths);
//	if (zr)
//		UnZipFileSystems.push_back(zr);
//	#ifdef _DEBUG
//	else
//	{
//		os::Printer::log("Could not open file. Folderfile not added", filename, ELL_ERROR);
//	}
//	#endif
//
//	_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
//	return (zr!=0);
//}


//! adds an zip archive to the filesystem
//bool CFileSystem::addZipFileArchive(const c8* filename, bool ignoreCase, bool ignorePaths)
//{
//	CZipReader* zr = 0;
//	IReadFile* file = createAndOpenFile(filename);
//	if (file)
//	{
//		zr = irrnew CZipReader(file, ignoreCase, ignorePaths);
//		if (zr)
//			ZipFileSystems.push_back(zr);
//
//		file->drop();
//	}
//	#ifdef _DEBUG
//	else
//		os::Printer::log("Could not open file. Zipfile not added", filename, ELL_ERROR);
//	#endif
//
//	_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
//	return (zr != 0);
//}


//! adds an pak archive to the filesystem
//bool CFileSystem::addPakFileArchive(const c8* filename, bool ignoreCase, bool ignorePaths)
//{
//	CPakReader* zr = 0;
//	IReadFile* file = createAndOpenFile(filename);
//	if (file)
//	{
//		zr = irrnew CPakReader(file, ignoreCase, ignorePaths);
//		if (zr)
//			PakFileSystems.push_back(zr);
//
//		file->drop();
//	}
//	#ifdef _DEBUG
//	else
//		os::Printer::log("Could not open file. Pakfile not added", filename, ELL_ERROR);
//	#endif
//
//	_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
//	return (zr != 0);
//}


bool CFileSystem::removeFileArchive(const c8* filename)
{
	/*for (int i = ZipFileSystems.size() - 1; i >= 0; --i)
	{
		if (strcmp(filename, ZipFileSystems[i]->getZipFileName()) == 0)
		{
			ZipFileSystems[i]->drop();
			ZipFileSystems.erase(i);
			return true;
		}
	}

	for (int i = PakFileSystems.size() - 1; i >= 0; --i)
	{
		if (strcmp(filename, PakFileSystems[i]->getPakFileName()) == 0)
		{
			PakFileSystems[i]->drop();
			PakFileSystems.erase(i);

			return true;
		}
	}

	for (int i = UnZipFileSystems.size() - 1; i >= 0; --i)
	{
		if (strcmp(filename, UnZipFileSystems[i]->getBaseFolderName()) == 0)
		{
			UnZipFileSystems[i]->drop();
			UnZipFileSystems.erase(i);

			return true;
		}
	}*/

	return false;
}


//! Returns the string of the current working directory
const c8* CFileSystem::getWorkingDirectory()
{
#if defined(_IRR_XBOX_PLATFORM_)
	strcpy(WorkingDirectory, "game:\\");
#elif defined(_IRR_PS3_PLATFORM_)
    //strcpy(WorkingDirectory, "/app_home/");
    return WorkingDirectory;
#endif
#if defined(_IRR_WINDOWS_API_)
	#if !defined ( _WIN32_WCE )
		_getcwd(WorkingDirectory, FILE_SYSTEM_MAX_PATH);
	#endif
#endif

#if (defined(_IRR_POSIX_API_) || defined(_IRR_OSX_PLATFORM_))
	getcwd(WorkingDirectory, (size_t)FILE_SYSTEM_MAX_PATH);
#endif
	return WorkingDirectory;
}


//! Changes the current Working Directory to the given string.
//! The string is operating system dependent. Under Windows it will look
//! like this: "drive:\directory\sudirectory\"
//! \return Returns true if successful, otherwise false.
bool CFileSystem::changeWorkingDirectoryTo(const c8* newDirectory)
{
	bool success=false;
//#if defined(_IRR_PS3_PLATFORM_)
//    UNIMPLEMENTED(__FUNCTION__);
//    return false;
//#elif defined (_MSC_VER)
//	#if defined(_IRR_XBOX_PLATFORM_)
//		UNIMPLEMENTED(__FUNCTION__);
//		return false;
//	#elif !defined ( _WIN32_WCE )
//		success=(_chdir(newDirectory) == 0);
//	#endif
//#elif defined(__MWERKS__)
//	UNIMPLEMENTED(__FUNCTION__);
//	return false;
//#else
//	success=(chdir(newDirectory) == 0);
//#endif
	return success;
}


#if defined(_IRR_PS3_PLATFORM_)
//!
//! Wrapper class for case-sensitive file system
//!
struct CS_FileEntry;

typedef map<stringc, CS_FileEntry*> CS_DirEntry;

struct CS_FileEntry
{
	stringc m_realPathName;
	CS_DirEntry m_entryList;
	bool isValid;

	stringc findFile(const stringc& filename, bool lowercase = false)
	{
		stringc fullpath(filename);
		if (!lowercase)
		{
			fullpath.replace('\\', '/');
			fullpath.make_lower();
		}
		if (m_realPathName.equals_ignore_case(fullpath))
		{
			return m_realPathName;
		}
		int pos = fullpath.findNext('/', m_realPathName.size());
		stringc entryName;
		bool subDir = false;
		if (pos >= 0)
		{
			// it's a subdirectory
			entryName = fullpath.subString(m_realPathName.size(), pos - m_realPathName.size());
			subDir = true;
		} else
		{
			// it's a file
			entryName = fullpath.subString(m_realPathName.size(), fullpath.size() - m_realPathName.size());
		}
		entryName.make_lower();

		CS_FileEntry* fileEntry = 0;
		if (m_entryList.find(entryName))
		{
			fileEntry = m_entryList[entryName];
		}
		if (!fileEntry)
		{
			DIR* hDir;
			dirent* hDirEnt;
			hDir = opendir(m_realPathName.c_str());
			do
			{
				hDirEnt = readdir(hDir); 
				if (hDirEnt) {
					if (hDirEnt->d_name[0] != '.') 
					{
						if (entryName.equals_ignore_case(stringc(hDirEnt->d_name)))
						{
							fileEntry = irrnew CS_FileEntry;
							fileEntry->m_realPathName = m_realPathName;
							fileEntry->m_realPathName += hDirEnt->d_name;
							if (subDir)
							{
								fileEntry->m_realPathName += "/";
							}
							fileEntry->isValid = true;
							m_entryList.insert(entryName, fileEntry);
							break;
						}
					}
				}
			}while (hDirEnt);

			closedir(hDir);
		}
		if (subDir && fileEntry && fileEntry->isValid)
		{
			return fileEntry->findFile(fullpath, true);
		} 
		if (fileEntry)
		{
			return fileEntry->m_realPathName;
		}

		fileEntry = irrnew CS_FileEntry;
		fileEntry->m_realPathName = "";
		fileEntry->isValid = false;
		m_entryList.insert(entryName, fileEntry);
		return stringc("");

	}
};

static CS_FileEntry s_CS_rootDir;
#endif

stringc CFileSystem::getAbsolutePath(const stringc& filename) const
{
	c8 *p=0;
#if defined(_IRR_XBOX_PLATFORM_)

	stringc realpath = "game:\\";
	realpath += filename;
	realpath.replace('/', '\\');
	//p = (c8*)filename.c_str();
	return realpath;

#elif defined(_IRR_WINDOWS_API_)
	#if !defined ( _WIN32_WCE )
		c8 fpath[_MAX_PATH];
		p = _fullpath( fpath, filename.c_str(), _MAX_PATH);
	#endif

#elif (defined(_IRR_POSIX_API_) || defined(_IRR_OSX_PLATFORM_))
	c8 fpath[4096];
	p = realpath(filename.c_str(), fpath);
	if(p == NULL)
	{
		p = (c8*)filename.c_str();	
	}
#else
	p = (c8*)filename.c_str();
#endif

	_IRR_DEBUG_BREAK_IF(p == 0);
	return stringc(p);
}


//! returns the directory part of a filename, i.e. all until the first
//! slash or backslash, excluding it. If no directory path is prefixed, a '.'
//! is returned.
stringc CFileSystem::getFileDir(const stringc& filename) const
{
	// find last forward or backslash
	s32 lastSlash = filename.findLast('/');
	const s32 lastBackSlash = filename.findLast('\\');
	lastSlash = lastSlash > lastBackSlash ? lastSlash : lastBackSlash;

	if ((u32)lastSlash < filename.size())
		return filename.subString(0, lastSlash);
	else
		return ".";
}


//! returns the base part of a filename, i.e. all except for the directory
//! part. If no directory path is prefixed, the full name is returned.
stringc CFileSystem::getFileBasename(const stringc& filename, bool keepExtension) const
{
	// find last forward or backslash
	s32 lastSlash = filename.findLast('/');
	const s32 lastBackSlash = filename.findLast('\\');
	lastSlash = max_(lastSlash, lastBackSlash);
	s32 end = 0;
	if (!keepExtension)
	{
		end = filename.findLast('.');
		if (end == -1)
			end=0;
		else
			end = filename.size()-end;
	}

	if ((u32)lastSlash < filename.size())
		return filename.subString(lastSlash+1, filename.size()-lastSlash-1-end);
	else if (end != 0)
		return filename.subString(0, filename.size()-end);
	else
		return filename;
}


//! Creates a list of files and directories in the current working directory 
//IFileList* CFileSystem::createFileList() const
//{
//	return irrnew CFileList();
//}


//! determines if a file exists and would be able to be opened.
bool CFileSystem::existFile(const c8* filename) const
{
	u32 i;

	/*for (i=0; i<ZipFileSystems.size(); ++i)
		if (ZipFileSystems[i]->findFile(filename)!=-1)
			return true;

	for (i=0; i<PakFileSystems.size(); ++i)
		if (PakFileSystems[i]->findFile(filename)!=-1)
			return true;

	for (i=0; i<UnZipFileSystems.size(); ++i)
		if (UnZipFileSystems[i]->findFile(filename)!=-1)
			return true;*/

	//FILE* f = fopen(filename, "rb");
	FILE* f = CFileSystem::open(filename, "rb");
	
	if (f) 
	{
		fclose(f);
		return true;
	}

	_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
	return false;
}


//! Creates a XML Reader from a file.
IXMLReader* CFileSystem::createXMLReader(const c8* filename)
{
	IReadFile* file = createAndOpenFile(filename);
	if (!file)
		return 0;

	IXMLReader* reader = createXMLReader(file);
	file->drop();
	return reader;
}


//! Creates a XML Reader from a file.
IXMLReader* CFileSystem::createXMLReader(IReadFile* file)
{
	if (!file)
		return 0;

	return createIXMLReader(file);
}


//! Creates a XML Reader from a file.
IXMLReaderUTF8* CFileSystem::createXMLReaderUTF8(const c8* filename)
{
	IReadFile* file = createAndOpenFile(filename);
	if (!file)
		return 0;

	IXMLReaderUTF8* reader = createIXMLReaderUTF8(file);
	file->drop();
	return reader;
}


//! Creates a XML Reader from a file.
IXMLReaderUTF8* CFileSystem::createXMLReaderUTF8(IReadFile* file)
{
	if (!file)
		return 0;

	return createIXMLReaderUTF8(file);
}


//! Creates a XML Writer from a file.
//IXMLWriter* CFileSystem::createXMLWriter(const c8* filename)
//{
//	IWriteFile* file = createAndWriteFile(filename);
//	IXMLWriter* writer = createXMLWriter(file);
//	file->drop();
//	return writer;
//}
//
//
////! Creates a XML Writer from a file.
//IXMLWriter* CFileSystem::createXMLWriter(IWriteFile* file)
//{
//	return irrnew CXMLWriter(file);
//}


//! creates a filesystem which is able to open files from the ordinary file system,
//! and out of zipfiles, which are able to be added to the filesystem.
IFileSystem* createFileSystem()
{
	return irrnew CFileSystem();
}


//! Creates a new empty collection of attributes, usable for serialization and more.
IAttributes* CFileSystem::createEmptyAttributes()
{
	return irrnew CAttributes();
}

c8 CFileSystem::WorkingDirectory[FILE_SYSTEM_MAX_PATH] = "";

FILE* CFileSystem::open(const char*filename, const char *attrib) {
#if defined(_IRR_PS3_PLATFORM_)
 
	s_CS_rootDir.m_realPathName = WorkingDirectory;
	stringc fullpath(WorkingDirectory);
	fullpath += filename;
    const char* suffix = 0;
    int len =strlen(filename);
    while(len > 0)
    {
        if(filename[len-1]=='.')
        {
            suffix = filename+len;
            break;
        }
        --len;
    }
    const void *pArg = 0;
    uint64_t    ArgSize = 0;
    int file;
    SceNpDrmOpenArg arg;
    arg.flag = SCE_NP_DRM_OPEN_FLAG;
    if(suffix && strcasecmp(suffix, "EDAT") == 0)
    {
        pArg = &arg;
        ArgSize = sizeof(SceNpDrmOpenArg);
    }
    int mode = CELL_FS_O_RDONLY;
    if(strchr(attrib,'r')!=0 && strchr(attrib,'w')!=0)
        mode = CELL_FS_O_RDWR|CELL_FS_O_CREAT;
	else if(strchr(attrib,'r')!=0)
		mode = CELL_FS_O_RDONLY;
	else if(strchr(attrib,'w')!=0)
		mode = CELL_FS_O_WRONLY | CELL_FS_O_CREAT;
    else
        _IRR_DEBUG_BREAK_IF(1);
    if(strchr(attrib,'a')!=0) mode|=CELL_FS_O_APPEND;
    int err = cellFsOpen(fullpath.c_str(), mode, &file, &arg, sizeof(arg));
    if (err != CELL_FS_SUCCEEDED) err = cellFsOpen(fullpath.c_str(), mode, &file, 0, 0);
    if (err != CELL_FS_SUCCEEDED) return 0;
        return fdopen(file,attrib);
#elif defined(_IRR_XBOX_PLATFORM_)
	stringc fn(filename);
	fn.replace('/', '\\');
	return fopen(fn.c_str(), attrib);
#else
	return fopen(filename, attrib);
#endif
}

#if defined(_IRR_PS3_PLATFORM_)
stringc CFileSystem::getPS3Path(const char*filename) {
 
	if (strlen(WorkingDirectory)) 
	{
		s_CS_rootDir.m_realPathName = WorkingDirectory;
		stringc fullpath(WorkingDirectory);
		fullpath += filename;

		FILE* file = fopen(fullpath.c_str(), "rb");
		if (!file) {
			stringc realPath = s_CS_rootDir.findFile(fullpath);
			if (realPath.size() > 0)
			{
				file = fopen(realPath.c_str(), "rb");
				if (file)
				{
					fclose(file);
					return realPath;
				}
			}
		}
		else
		{
			fclose(file);
			return fullpath;
		}
	}	
	return stringc("");
}
#endif

#if defined(_IRR_PS3_PLATFORM_)
SceNpDrmKey CFileSystem::NpDrmKey = {
	{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }
};	
void  CFileSystem::setNpDrmKey(const SceNpDrmKey& npDrmKey)
{
	memcpy(NpDrmKey.keydata, npDrmKey.keydata, sizeof(NpDrmKey.keydata));
}

const SceNpDrmKey& CFileSystem::getNpDrmKey()
{
	return NpDrmKey;
}

#endif

